//=============================================================================

#include <cctype>
#include <cmath>
#include <iostream>
#include <string>
/////////////////////////////////////////////
#include "tinyxml.h"
/////////////////////////////////////////////
#include "log.hpp"
#include "filemanager.hpp"
/////////////////////////////////////////////
#include "bitfont.hpp"
#include "frames.hpp"

using namespace std;

namespace KEG
{

bool BitFont::Load( const std::string& filename, int padding_ )
{
	LOG_RAW() << "Loading BitFont: " << filename << std::endl;

	TiXmlDocument *xmlDoc = new TiXmlDocument(filename);
	if (!xmlDoc->LoadFile())
	{
		// Fatal error, cannot load
		LOG_ERROR( "BitFont file not found (follows)" );
		//LOG_RAW() << filename << std::endl;
		return 0;
	}

	// Grab the outer element
	TiXmlElement *outerNode = xmlDoc->FirstChildElement("bitfont");
	if( !outerNode )
	{
		LOG_ERROR( "BitFont XML broken" );
		return 0;
	}

	// Grab the name of the frames file
	std::string framesFile = outerNode->Attribute("frames");

	// Arguments for loading frames/tex
	//vector< float > texData;
	//int width=0, height=0;
	//Texture texInfo;

	if( false == LoadTilesAndTex( Path( FRAME, framesFile), texData, this->texture ) )
	{
		LOG_ERROR( "Loading Frames for BitFont FAILED." );
		return false;
	}

	//tex = texInfo.Tex;
	//width = texInfo.Width;
	//height = texInfo.Height;

	assert( texture.Alpha && !texture.Blend );

	// compute the size of each frame (assume at least one frame)
	assert( texData.Coord.size() >= 4 );
#pragma
	sizew = nativew = floor((texData.Coord[2] - texData.Coord[0]) + 0.5f);
	sizeh = nativeh = floor((texData.Coord[2] - texData.Coord[0]) + 0.5f);
#ifdef PATHWAY
	if( !Global::OGLCaps::NonPow2 && Global::OGLCaps::TexRect )
	{
		sizew = nativew = floor((texData[2] - texData[0]) + 0.5f);
		sizeh = nativeh = floor((texData[2] - texData[0]) + 0.5f);
	}
	else
	{
		sizew = nativew = floor((texData[2] - texData[0])*width + 0.5f);
		sizeh = nativeh = floor((texData[2] - texData[0])*width + 0.5f);
	}
#endif

	TiXmlElement *charNode = outerNode->FirstChildElement( "char" );
	if( !charNode )
	{
		LOG_ERROR( "BitFont XML broken" );
		return 0;
	}

	// Load in the valid characters

	char start, end;
	while( charNode )
	{
		const char* startStr = charNode->Attribute( "start" );
		const char* endStr = charNode->Attribute( "end" );

		assert( startStr[0] != 0 );
		assert( endStr[0] != 0 );

		start = startStr[0];
		end = endStr[0];

		if( validChar.empty() )
		{
			for(char i = start; i <= end; ++i)
			{
				validChar.push_back( i );
			}
		}
		else
		{
			if( end < validChar.front() )
			{
				for( char i = end; i >= start; --i)
				{
					validChar.push_front( i );
				}
			}
			else if ( start > validChar.back() )
			{
				for( char i = start; i <= end; ++i )
				{
					validChar.push_back( i );
				}
			}
			else
			{
				LOG_ERROR( "BitFont Error, Char Overlap" );
				validChar.clear();
				return 0;
			}
		}

		// next node
		charNode = charNode->NextSiblingElement("char");

	}

	// check we have valid frame:char ratio
	assert( (float)validChar.size() == float(texData.Coord.size())/4 );

	// Create the display lists
	Reset( nativew, nativeh, padding_ );

	// Close and delete file
	xmlDoc->Clear();
	delete xmlDoc;
	return 1;

}

void BitFont::Reset( int sizew_, int sizeh_, int padding_ )
{
	// Check for updates to attributes
	if( sizew_ != 0 )
		sizew = sizew_;
	else
		sizew = nativew;

	if( sizeh_ != 0 )
		sizeh = sizeh_;
	else
		sizew = nativew;

	padding = padding_;

	// Delete old display list
	if( !dispListIndex.empty() )
	{
		glDeleteLists( dispListIndex.front(), dispListIndex.size() );
		dispListIndex.clear();
	}

	// Compile the display list for each valid char
	GLuint listStart = glGenLists( validChar.size() );

	//
	char lastChar = validChar.front() - 1;
	GLuint currDispList = listStart;

	for( int currChar = 0; currChar < validChar.size(); ++currChar )
	{
		// Pad in-between characters if necessary
		for( int i = lastChar; i < validChar[currChar] ; ++i )
		{
			dispListIndex.push_back( currDispList );
		}
		
		// Compile the list
		glNewList( currDispList, GL_COMPILE );

		glBegin( GL_QUADS );

		// UPPER LEFT
		glTexCoord2f( texData.Coord[ currChar*4 + 0 ], texData.Coord[ currChar*4 + 1 ] );
		glVertex2f( 0.0f, 0.0f );

		// UPPER RIGHT
		glTexCoord2f( texData.Coord[ currChar*4 + 2 ], texData.Coord[ currChar*4 + 1 ] );
		glVertex2f( sizew, 0.0f );

		// LOWER RIGHT
		glTexCoord2f( texData.Coord[ currChar*4 + 2 ], texData.Coord[ currChar*4 + 3 ] );
		glVertex2f( sizew, sizeh );

		// LOWER LEFT
		glTexCoord2f( texData.Coord[ currChar*4 + 0 ], texData.Coord[ currChar*4 + 3 ] );
		glVertex2f( 0.0f, sizeh );

		glEnd();

		// TRANSLATE TO NEXT POSITION
		glTranslatef( sizew + padding, 0.0f, 0.0f );

		glEndList();

		// Prepare next loop
		lastChar = validChar[currChar];
		++currDispList;
	}

	// check for errors
	int error = glGetError();
	assert( error == GL_NO_ERROR );

}

void BitFont::RenderText( const std::string& str, float x, float y )
{
	//glBegin(GL_QUADS);
	//glTexCoord2f( 0.0f, 0.0f );
	//glVertex2f  ( 0.0f, 0.0f );
	//glTexCoord2f( 1.0f, 0.0f );
	//glVertex2f  ( 128.0f, 0.0f );
	//glTexCoord2f( 1.0f, 1.0f );
	//glVertex2f  ( 128.0f, 128.0f );
	//glTexCoord2f( 0.0f, 1.0f );
	//glVertex2f  ( 0.0f, 128.0f );
	//glEnd();
	//glPushMatrix();
	//glTranslatef( 128.0f, 0.0f, 0.0f );
	//glBegin(GL_QUADS);
	//glTexCoord2f( 0.0f, 0.0f );
	//glVertex2f  ( 0.0f, 0.0f );
	//glTexCoord2f( 1.0f, 0.0f );
	//glVertex2f  ( 128.0f, 0.0f );
	//glTexCoord2f( 1.0f, 1.0f );
	//glVertex2f  ( 128.0f, 128.0f );
	//glTexCoord2f( 0.0f, 1.0f );
	//glVertex2f  ( 0.0f, 128.0f );
	//glEnd();
	//glPopMatrix();
	//return;

	x = floor(x);
	y = floor(y);
	
	char lowestChar = validChar.front();
	char highestChar = validChar.back();

	// bind the texture
#pragma
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texture.Tex );
#ifdef PATHWAY
	if( !Global::OGLCaps::NonPow2 && Global::OGLCaps::TexRect )
		glBindTexture( GL_TEXTURE_RECTANGLE_ARB, tex );
	else
		glBindTexture( GL_TEXTURE_2D, tex );
#endif

	// translate
	glPushMatrix();
	glTranslatef( x, y, 0.0f );

	for( int i = 0; i < str.size(); ++i )
	{
		if( str[i] >= lowestChar && str[i] <= highestChar )
		{
			glCallList( dispListIndex[ (int)str[i] - lowestChar ] );
		}
	}

	// pop
	glPopMatrix();
}

void BitFont::RenderTextCentered( const std::string& str, float y, float totalwidth )
{
	RenderText( str, totalwidth/2 - CalcStringWidth( str )/2, y );
}

// std::transform(myString.begin(), myString.end(), myString.begin(), toupper);

void BitFont::Release()
{
	if( validChar.size() > 0 && dispListIndex.size() > 0 )
	{
		int error = glGetError();
		assert( error == GL_NO_ERROR );
		glDeleteLists( dispListIndex.front(), validChar.size() );
		error = glGetError();
		assert( error == GL_NO_ERROR );
		//glDeleteTextures( 1, &tex );
		//error = glGetError();
		//assert( error == GL_NO_ERROR );
		texture.Release();

		dispListIndex.clear();
		validChar.clear();
		texData.Coord.clear();
	}
}

BitFont::~BitFont()
{
	Release();
}

}
